package com.ejzblog.mall.utils;

import com.alibaba.fastjson.JSONObject;
import com.ejzblog.mall.exceptionhandler.DataException;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import weixin4j.WeixinException;
import weixin4j.http.HttpsClient;
import weixin4j.http.Response;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.security.spec.AlgorithmParameterSpec;

import static com.ejzblog.mall.config.WeChatPayConfig.APP_ID;
import static com.ejzblog.mall.config.WeChatPayConfig.APP_SECRET;
import static com.ejzblog.mall.config.WeChatPayConfig.GET_INFO;
import static com.ejzblog.mall.config.WeChatPayConfig.GRANT_TYPE;
import static com.ejzblog.mall.constant.Constant.OPEN_ID;
import static com.ejzblog.mall.constant.Constant.SESSION_KEY;
import static com.ejzblog.mall.enums.ResponseStatusEnum.WE_CHAT_DECRYPT_ERROR;
import static com.ejzblog.mall.enums.ResponseStatusEnum.WE_CHAT_SERVER_ERROR;

/**
 * <p>
 * Description：微信工具类
 * </p>
 *
 * @author Mango
 * @version v1.0.0
 * @ProjectName: community-mall-master
 * @Package: com.ejzblog.mall.utils
 * @date 2021-07-13 16:04
 * @see com.ejzblog.mall.utils
 */
@Slf4j
public class WeChatUtils {

    /**
     * 获取微信openid
     *
     * @param code 用户允许登录后，回调内容会带上 code（有效期五分钟），开发者需要将 code 发送到开发者服务器后台，使用code 换取 session_key api，将 code 换成 openid 和 session_key
     * @return 字符串
     */
    public String weChatAuth(String code) {

        HttpsClient hc = new HttpsClient();

        String openid = null;

        try {
            // 发送请求到微信服务器换取用户OpenId
            Response rs = hc.get(GET_INFO + APP_ID + "&secret=" + APP_SECRET + "&js_code=" + code + GRANT_TYPE);

            // 解析微信服务器发回来的微信用户信息
            JSONObject json = rs.asJSONObject();

            openid = json.getString(OPEN_ID);
            log.info("拿到的微信授权OpenId：{}", openid);

            if (openid == null) {
                throw new DataException(WE_CHAT_SERVER_ERROR.getMessage(), null);
            }
        } catch (WeixinException ex) {
            ex.printStackTrace();
            log.error(ex.getMessage());
            throw new DataException(WE_CHAT_SERVER_ERROR.getMessage(), null);
        }
        return openid;
    }

    /**
     * 获取微信用户手机号
     *
     * @param code          用户允许登录后，回调内容会带上 code（有效期五分钟），开发者需要将 code 发送到开发者服务器后台，使用code 换取 session_key api，将 code 换成 openid 和 session_key
     * @param encryptedData 明文,加密数据
     * @param iv            加密算法的初始向量
     * @return 字符串
     */
    public String weChatAuthPhone(String code, String encryptedData, String iv) {

        HttpsClient hc = new HttpsClient();

        String phoneNumber = null;

        try {

            try {
                // 发送请求到微信服务器换取用户OpenId
                Response rs = hc.get(GET_INFO + APP_ID + "&secret=" + APP_SECRET + "&js_code=" + code + GRANT_TYPE);

                // 解析微信服务器发回来的微信用户信息
                JSONObject json = rs.asJSONObject();

                String sessionkey = json.get(SESSION_KEY).toString();
                byte[] encrypData = Base64.decodeBase64(encryptedData);
                byte[] ivData = Base64.decodeBase64(iv);
                byte[] sessionKey = Base64.decodeBase64(sessionkey);

                try {
                    JSONObject jsonobject = JSONObject.parseObject(decrypt(sessionKey, ivData, encrypData));
                    // 拿到用户手机号
                    phoneNumber = jsonobject.getString("phoneNumber");
                    log.info("拿到的微信用户手机号：{}", phoneNumber);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new DataException(WE_CHAT_DECRYPT_ERROR.getMessage(), "" + e);
                }

            } catch (WeixinException wx) {
                throw new WeixinException("获取openid时，无法访问到微信服务器！");
            }
        } catch (WeixinException ex) {
            ex.printStackTrace();
            log.error(ex.getMessage());
            throw new DataException(WE_CHAT_SERVER_ERROR.getMessage(), null);
        }
        return phoneNumber;
    }

    /**
     * 解密
     *
     * @param key     密钥
     * @param iv      加密算法的初始向量
     * @param encData 明文,加密数据
     * @return {@link String}
     * @throws Exception
     */
    private static String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        // 解析解密后的字符串
        return new String(cipher.doFinal(encData), "UTF-8");
    }

}
